/**
 * Copyright 2011 Joe Choi, All rights reserved.
 */
package tao.calendar.gregorian;

import tao.time.Time;

/**
 * The ConcreteOperatableTime class is the concrete implementation of OperatableTime interface
 * 
 * @author Joe Choi
 * @version 1.0.0
 */
class ConcreteOperatableTime extends ConcreteGregorianTime implements OperatableTime {
	public ConcreteOperatableTime(GregorianTime time) {
		super(time);
	}
	
	/**
	 * roll amount of time to specific unit 
	 * @see Operatable
	 */
	public Operatable roll(GregorianUnitAmount amount) {
		int hr = getHour();
		int min = getMinute(); 
		int sec = getSecond();
		int ms = getMillisecond();
		
		switch(amount.getUnit()) {
			case Hour : hr += amount.getValue(); break; 
			case Minute : min += amount.getValue(); break; 
			case Second : sec += amount.getValue(); break; 
			case Millisecond : ms += amount.getValue(); break; 
		}
		
		normalize(hr, min, sec, ms);
		return(this);
	}

	
	/**
	 * add given amount of time to specific unit
	 * @see Operatable
	 */
	public Operatable add(GregorianUnitAmount amount) {
		if(amount.getUnit() == Gregorian.Unit.Hour || 
		   amount.getUnit() == Gregorian.Unit.Minute ||
		   amount.getUnit() == Gregorian.Unit.Second ||
		   amount.getUnit() == Gregorian.Unit.Millisecond) {
			construct(scalar() + scalar(amount));
		}
		return(this);
	}
	
	/**
	 * Calculate the different of the time
	 * @see Operatable
	 */
	public GregorianAmount diff(Time otherTime) {
		return(constructAmount(Math.abs(scalar() - otherTime.scalar())));
	}
	
	private long scalar(GregorianUnitAmount amt) {
		switch(amt.getUnit()) {
			case Hour: return(amt.getValue() * GregorianTime.HOUR_MILLIS);	
			case Minute: return(amt.getValue() * GregorianTime.MINUTE_MILLIS);	
			case Second: return(amt.getValue() * GregorianTime.SECOND_MILLIS);	
			case Millisecond: return(amt.getValue());
			default : return(0);
		}
	}
	
	private static GregorianAmount constructAmount(long scalar) {
		int hr, min, sec, ms;

		scalar %= DAY_MILLIS;
	
		hr = (int) (scalar / GregorianTime.HOUR_MILLIS);
		scalar %= GregorianTime.HOUR_MILLIS;
		
		min = (int) (scalar / GregorianTime.MINUTE_MILLIS);
		scalar %= GregorianTime.MINUTE_MILLIS;
		
		sec = (int) (scalar / GregorianTime.SECOND_MILLIS);		
		ms = (int) (scalar % GregorianTime.SECOND_MILLIS);
		
		return(Gregorian.timeAmount(hr, min, sec, ms));
	}
	
	static GregorianAmount constructTimeAmount(long scalar) {
		return(constructAmount(scalar));
	}
}
